//go:generate go run main.go
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

// TestCase defines a single usage limit test configuration
type TestCase struct {
	NodeCount           int
	TotalCredits        int64
	CostPerRequest      int64
	LoadFactor          float64
	TestDurationSeconds int
}

var extremeEdgeCases = []TestCase{
	{NodeCount: 9, TotalCredits: 1000, CostPerRequest: 1, LoadFactor: 3.0, TestDurationSeconds: 15},
	{NodeCount: 9, TotalCredits: 500, CostPerRequest: 5, LoadFactor: 2.0, TestDurationSeconds: 15},
}

var realisticCombinations = []TestCase{
	// Single node tests
	{NodeCount: 1, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 0.9, TestDurationSeconds: 30},
	{NodeCount: 1, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 1.0, TestDurationSeconds: 30},
	{NodeCount: 1, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 2.0, TestDurationSeconds: 30},

	// Multi-node with standard cost
	{NodeCount: 3, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 0.9, TestDurationSeconds: 30},
	{NodeCount: 5, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 0.9, TestDurationSeconds: 30},
	{NodeCount: 3, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 1.0, TestDurationSeconds: 30},
	{NodeCount: 5, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 1.0, TestDurationSeconds: 30},
	{NodeCount: 3, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 2.0, TestDurationSeconds: 30},
	{NodeCount: 5, TotalCredits: 100, CostPerRequest: 1, LoadFactor: 2.0, TestDurationSeconds: 30},

	// Variable cost tests
	{NodeCount: 3, TotalCredits: 1000, CostPerRequest: 10, LoadFactor: 0.9, TestDurationSeconds: 20},
	{NodeCount: 5, TotalCredits: 1000, CostPerRequest: 10, LoadFactor: 1.5, TestDurationSeconds: 20},
	{NodeCount: 3, TotalCredits: 500, CostPerRequest: 5, LoadFactor: 1.0, TestDurationSeconds: 25},
	{NodeCount: 5, TotalCredits: 500, CostPerRequest: 5, LoadFactor: 2.0, TestDurationSeconds: 25},

	// Low credit, high accuracy tests
	{NodeCount: 1, TotalCredits: 10, CostPerRequest: 1, LoadFactor: 0.8, TestDurationSeconds: 15},
	{NodeCount: 3, TotalCredits: 10, CostPerRequest: 1, LoadFactor: 1.2, TestDurationSeconds: 15},
	{NodeCount: 5, TotalCredits: 10, CostPerRequest: 1, LoadFactor: 1.5, TestDurationSeconds: 15},

	// High cost per request
	{NodeCount: 3, TotalCredits: 100, CostPerRequest: 20, LoadFactor: 1.0, TestDurationSeconds: 10},
	{NodeCount: 5, TotalCredits: 100, CostPerRequest: 20, LoadFactor: 1.5, TestDurationSeconds: 10},
}

// BuildTag returns the go:build constraint line (or empty) appropriate for tc.
func (tc TestCase) BuildTag() string {
	switch {
	// Single nodes always run
	case tc.NodeCount == 1:
		return "//go:build integration"
	// NodeCount up to and including 5 uses the integration_long tag
	case tc.NodeCount <= 5:
		return "//go:build integration_long"
	// Assume its a stress test to ensure fast tests stay fast
	default:
		return "//go:build stress"
	}
}

func (tc TestCase) PackageName() string {
	return fmt.Sprintf("usagelimit_nodes%02d_credits%04d_cost%02d_load%s_duration%03d",
		tc.NodeCount,
		tc.TotalCredits,
		tc.CostPerRequest,
		strings.ReplaceAll(fmt.Sprintf("%05.2f", tc.LoadFactor), ".", "_"),
		tc.TestDurationSeconds,
	)
}

func (tc TestCase) TestName() string {
	return fmt.Sprintf("TestIntegration_UsageLimit_Nodes%02d_Credits%04d_Cost%02d_Load%s_Duration%03d",
		tc.NodeCount,
		tc.TotalCredits,
		tc.CostPerRequest,
		strings.ReplaceAll(fmt.Sprintf("%05.2f", tc.LoadFactor), ".", "_"),
		tc.TestDurationSeconds,
	)
}

const testTemplate = `// Code generated by go generate; DO NOT EDIT.{{ if .BuildTag }}
{{ .BuildTag }}
{{ end }}
package {{ .PackageName }}

import (
	"testing"

	"github.com/unkeyed/unkey/go/apps/api/integration"
	run "github.com/unkeyed/unkey/go/apps/api/integration/multi_node_usagelimiting"
	"github.com/unkeyed/unkey/go/pkg/testutil"
)

func {{ .TestName }}(t *testing.T) {
	testutil.SkipUnlessIntegration(t)

	h := integration.New(t, integration.Config{
		NumNodes: {{ .NodeCount }},
	})

	run.RunUsageLimitTest(
		t,
		h,
		{{ .TotalCredits }},           // total credits
		{{ .CostPerRequest }},         // cost per request
		{{ .LoadFactor }},             // load factor
		{{ .NodeCount }},              // node count
		{{ .TestDurationSeconds }},    // test duration seconds
	)
}
`

func main() {
	tmpl := template.Must(template.New("test").Parse(testTemplate))

	// Create base directory
	baseDir := "../generated"
	err := os.MkdirAll(baseDir, 0755)
	if err != nil {
		panic(err)
	}

	allCases := append(realisticCombinations, extremeEdgeCases...)

	for _, tc := range allCases {
		// Create package directory
		packageDir := filepath.Join(baseDir, tc.PackageName())
		err := os.MkdirAll(packageDir, 0755)
		if err != nil {
			panic(err)
		}

		// Create test file
		testFile := filepath.Join(packageDir, "generated_test.go")
		f, err := os.Create(testFile)
		if err != nil {
			panic(err)
		}

		err = tmpl.Execute(f, tc)
		if err != nil {
			panic(err)
		}

		f.Close()
		fmt.Printf("Generated %s\n", testFile)
	}

	fmt.Printf("Generated %d test files\n", len(allCases))
}
